export * from "./EventBus";
import { assign, findById, findIndexById, isNS, isNst, isNull, isNumber, isArr, isObj, request } from "./EventBus";
export class BA<T extends ki> extends Array<T> {
  readonly onChanged: cbk = () => { };
  constructor (onChanged?: cbk) {
    super();
    if (onChanged) {
      this.onChanged = onChanged;
    }
  }
  toArr(p?: T[]) {
    if (!Array.isArray(p)) {
      p = this;
    }
    const r: T[] = [];
    p.forEach(e => {
      r.push(e);
    });
    return r;
  }
  //#region push
  push(): number;
  /** 添加的值具有唯一性,如果传入的值是object.则会对比id; 如果现有数据中存在,则会覆盖现有数据的各项属性.如果不存在此数据则会正常添加 */
  push(...itemss: T[][]): number;
  /** 添加的值具有唯一性,如果传入的值是object.则会对比id; 如果现有数据中存在,则会覆盖现有数据的各项属性.如果不存在此数据则会正常添加 */
  push(...items: T[]): number;
  push(...items: T[] | T[][]): number {
    items.forEach((p?: T | T[]) => {
      if (p) {
        if (Array.isArray(p)) {
          this.push(...p);
        } else {
          // @
          const r = this.find(e => e.id == p.id);
          if (r) {
            assign(r, p);
          } else {
            super.push(p);
          }
        }
      }
    });
    this.onChanged();
    return this.length;
  }
  //#endregion push
  //#region splice
  splice(start: number, deleteCount?: number): T[];
  splice(start: number, deleteCount: number, ...items: T[]): T[];
  splice(...p: any): T[] {
    // @ts-ignore
    const res = super.splice(...p);
    this.onChanged();
    return this.toArr(res);

  }
  //#endregion splice
  shift(): T | undefined {
    const res = super.shift();
    this.onChanged();
    return res;
  }
  pop(): T | undefined {
    const res = super.pop();
    this.onChanged();
    return res;
  }
  unshift(...items: T[]): number {
    const res = super.unshift(...items);
    this.onChanged();
    return res;
  }
  slice(start?: number, end?: number): T[] {
    const r: T[] = [];
    r.push(...super.slice(start, end));
    return r;
  }
  //#region findById
  findById(id?: string): undefined | T;
  findById(id: string[]): T[];
  findById(id?: string | string[]) {
    if (Array.isArray(id)) {
      const ret: T[] = [];
      id.forEach(i => {
        const t = findById(this as T[], i);
        if (t) {
          ret.push(t);
        }
      });
      return ret;
    }
    return findById(this as T[], id);
  }
  //#endregion
  /** 重写filter,增加一个this指向的参数,方便predicate内部取得this值进行对比 */
  filter(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): T[] {
    const ret: T[] = [];
    const _arg2 = thisArg || window;
    this.forEach((e, i) => {
      if (predicate.apply(_arg2, [e, i, this as T[]])) {
        ret.push(e);
      }
    });
    return ret;
  }
}
/** 具有本地缓存功能的Array,并且重写push方法,只会加入唯一的值. */
export class BaseArr<Type extends string, T extends ki> extends BA<T> {
  private readonly _cfg: Required<BaseArrOptios<Type, T>>;
  private readonly _cache: CacheBaseArr<T> = { p: false, r: [], rc: [], d: [], dc: [], };

  constructor (options: BaseArrOptios<Type, T>) {
    const onChanged = options.onChanged || (() => { });
    super(onChanged);
    const dc = { rid() { }, c() { }, u() { }, r() { }, d() { }, onChanged };
    this._cfg = assign(dc, isObj(options) ? options : {} as any);
  }
  //#region method
  //#region getById
  /** 可传入id的值进行查找本地缓存数据,如果缓存没有,会触发向服务器发起请求;如果real值为非0,则跳过本地缓存 */
  getById(id: string, real?: boolean): Promise<T | undefined>;
  /** 可传入id的值(数组) 进行查找本地缓存数据,如果缓存没有,会触发向服务器发起请求;如果real值为非0,则跳过本地缓存 */
  getById(ids: string[], real?: boolean): Promise<T[]>;
  getById(): Promise<undefined>;
  getById(param?: string | string[], real?: boolean): Promise<T | T[] | undefined> {
    return new Promise<T | T[] | undefined>(async (resolve) => {
      if (!isNull(param)) {
        if (Array.isArray(param)) {
          const r: T[] = [],
            id = param.filter(k => isNst(k));
          if (!real && id.length) {
            this.forEach((e) => {
              let i: number;
              if ((i = id.indexOf(e.id)) >= 0) {
                id.splice(i, 1);
                r.push(e);
              }
            });
          }
          if (id.length) {
            if (this._cache.p) {
              this._cache.r.push(...id);
              this._cache.rc.push(rs => {
                rs.forEach((e) => { r.push(e); });
                resolve(r);
              });
            } else {
              const rs = await this._cfg.rid(id, this._cfg.type, this);
              rs.forEach((e) => { r.push(e); });
              resolve(r); this.push(r);
            }
          } else {
            resolve(r);
          }
          return;
        } else if (isNS(param)) {
          if (!real) {
            const r = this.find((e) => e.id == param);
            if (r) return resolve(r);
          }
          if (this._cache.p) {
            this._cache.r.push(param);
            this._cache.rc.push(rs => {
              resolve(rs.find(e => e.id == param));
            });
          } else {
            const rs = await this._cfg.rid(param, this._cfg.type, this);
            if (rs && Array.isArray(rs)) {
              this.push(rs);
              resolve(rs.find(e => e.id == param));
            } else {
              resolve(undefined);
            }
          }
          return;
        }
      }
      resolve(undefined);
    });
  };
  //#endregion
  /** 从本地缓存中查找符合id值的数据 */
  findIndexById(id?: string) {
    return findIndexById(this, id);
  }
  /** 发送请求 新增数据 */
  async create(entity: T) {
    const r = await this._cfg.c(entity, this._cfg.type, this);
    if (r && r["id"]) {
      this.push(r as T);
    }
    return r;
  }
  /** 发送请求 修改数据 */
  update(entity: UpdateParam<T>) {
    return this._cfg.u(entity, this._cfg.type, this);
  }
  /** 发送请求 查询数据 */
  read(): Promise<T[]>;
  read(param: string): Promise<T>;
  read(param: string[]): Promise<T[]>;
  read(param: { id: string;[k: string]: any; }): Promise<T>;
  read(param: { id: string[];[k: string]: any; }): Promise<T[]>;
  read(param: QueryPage<T>): Promise<Page<T>>;
  read(param: Rp<T>): Promise<T[]>;
  read(param: Partial<T>): Promise<T[]>;
  async read(param?: FQ<T> | string | string[]) {

    const r: any = typeof param == "string" ? await this.getById(param) :
      Array.isArray(param) ? await this.getById(param) :
        await this._cfg.r(param || {}, this._cfg.type, this);

    if (Array.isArray(r) && r.length) {
      this.push(r);
    } else if (isNumber(r.current) && isNumber(r.total) && isNumber(r.size) && isArr(r.data)) {
      this.push(r.data);
    } else if (isNst(r.id)) {
      this.push(r);
    }
    return r;
  }
  /** 发送请求 根据id删除数据 */
  deleteById(id: string) {
    return new Promise<number>((resolve) => {
      if (this._cache.p) {
        this._cache.d.push(id);
        this._cache.dc.push(resolve);
      } else {
        resolve(this._cfg.d({ id }, this._cfg.type, this));
      }
    });
  }
  /** 发送请求 根据条件删除数据 */
  public delete(param: FQ<T>) {
    return this._cfg.d(param, this._cfg.type, this);
  }
  /** 发送请求 获取所有pid为参数值的数据 */
  public async getParents(pid: string | string[]): Promise<T[]> {
    const ret: T[] = [];
    if (Array.isArray(pid)) {
      for (let i = 0; i < pid.length; i++) {
        (await this.getParents(pid[i])).forEach(e => { ret.push(e); });
      }
    } else {
      const c = await this.getById(pid);
      if (c) {
        (await this.getParents(c.pid)).forEach(e => { ret.push(e); });
        ret.push(c);
      }
    }

    return ret;
  }
  //#endregion method
  /** 暂停 读取/删除 的请求发送 */
  pause() {
    this._cache.p = true;
  }
  /** 开始正常请求 所有请求完毕后,将返回 */
  exec() {
    this._cache.p = false;
    let i = 0;
    return new Promise<void>((resolve, reject) => {
      if (this._cache.r.length) {
        i++;
        this._cfg.rid(this._cache.r, this._cfg.type, this).then((re) => {
          this._cache.rc.forEach(rc => { try { rc(re); } catch (_) { } });
          if (--i < 1) { resolve(); }
        }).catch((e) => { if (--i < 1) { reject(e); } });
        this._cache.r.splice(0);
      }
      if (this._cache.d.length) {
        i++;
        this._cfg.d({ id: this._cache.r }, this._cfg.type, this).then((re) => {
          this._cache.dc.forEach(dc => { try { dc(re); } catch (_) { } });
          if (--i < 1) { resolve(); }
        }).catch((e) => { if (--i < 1) { reject(e); } });
        this._cache.d.splice(0);
      }
    });
  }
}
function makeCreate(p: obj) {
  const removeKeys = ["id", "createTime", "changeTime", "create"];
  Object.keys(p).forEach(k => {
    if (k.startsWith("_") || (removeKeys.includes(k) && (!p[k] || p[k] === "0"))) {
      delete p[k];
    }
  });
  return p;
}
export class BaseArrSimple<Type extends string, T extends ki> extends BaseArr<Type, T> {
  constructor (type: Type, format: (p?: FormatParam<T>) => any) {
    super({
      type,
      rid: async (ids, type, _this) => { return ids ? await format(await request(`${type}/read`, { id: ids })) : undefined; },
      c: async (e, type, _this) => { return await format(await request(`${type}/create`, makeCreate(e)) as T); },
      u: async (e, type, _this) => { return await request(`${type}/update`, e) as number; },
      r: async (e, type, _this) => { return await format(await request(`${type}/read`, e)); },
      d: async (e, type, _this) => { return await request(`${type}/delete`, e) as number; },
    });
  }
}